/**
 *  author:        Timothy Groves - http://www.brandspankingnew.net
 *    version:    1.2 - 2006-11-17
 *              1.3 - 2006-12-04
 *              2.0 - 2007-02-07
 *
 * 
 Original Page: http://www.brandspankingnew.net/archive/2007/02/ajax_auto_suggest_v2.html
       License: Creative commons license.

       
 Notes: 
    The original source was modified to used on Chrome Extension, which has limitation regarding events and cross domain requests.
    It is also not possible to call any ajax function from content page.
    Marco Aurelio Zoqui. 
 */ 
var useBSNns;
if (useBSNns)
{
    if (typeof(bsn) == "undefined")
        bsn = {}
    _bsn = bsn;
}
else
{
    _bsn = this;
}

if (typeof(_bsn.Autosuggest) == "undefined")
    _bsn.Autosuggest = {}

_bsn.AutoSuggest = function (fldID, param)
{
    // no DOM - give up!
    //
    if (!document.getElementById)
        return false;        
    // get field via DOM
    //
    this.fld = _bsn.DOM.getElement(fldID);
    if (!this.fld)
        return false;    
    
    // init variables
    //
    this.sInput         = "";
    this.nInputChars     = 0;
    this.aSuggestions     = [];
    this.iHighlighted     = 0;    
    // parameters object
    //
    this.oP = (param) ? param : {};
    
	
    // defaults    
    //
    if (!this.oP.minchars)              this.oP.minchars = 1;
    if (!this.oP.method)                this.oP.meth = "get";
    if (!this.oP.varname)               this.oP.varname = "input";
    if (!this.oP.className)             this.oP.className = "autosuggest";
    if (!this.oP.timeout)               this.oP.timeout = 2500;
    if (!this.oP.delay)                 this.oP.delay = 100;
    if (!this.oP.offsety)               this.oP.offsety = 0;
    if (!this.oP.shownoresults)         this.oP.shownoresults = true;
    if (!this.oP.noresults)             this.oP.noresults = 'No Results';
    if (!this.oP.noresultslink)			this.oP.noresultslink = '';
	
	
    if (!this.oP.maxheight && this.oP.maxheight !== 0)        this.oP.maxheight = 250;
    if (!this.oP.cache && this.oP.cache != false)            this.oP.cache = true;
    if (!this.oP.searchFunction)            				this.oP.searchFunction = null;
    
    // set keyup handler for field
    // and prevent autocomplete from client
    //
    var pointer = this;
    
    // NOTE: not using addEventListener because UpArrow fired twice in Safari
    //_bsn.DOM.addEvent( this.fld, 'keyup', function(ev){ return pointer.onKeyPress(ev); } );
    
    this.fld.onkeypress = function(ev){ return pointer.onKeyPress(ev); }
    this.fld.onkeyup = function(ev){ return pointer.onKeyUp(ev); }
    
    this.fld.onblur = function(ev){
        ev.stopPropagation();
        //this.fld.focus();
        return false;
    }

 
    
    this.fld.setAttribute("autocomplete","off");
}

_bsn.AutoSuggest.prototype.onKeyPress = function(ev)
{ 
    var key = (window.event) ? window.event.keyCode : ev.keyCode;
    var RETURN = 13;
    var TAB    =  9;
    var ESC    = 27;    
    var ARRUP  = 38;
    var ARRDN  = 40;
    var bubble = true;
    var ele = _bsn.DOM.getElement("as_"+String(this.fld.seq));
    switch(key){
        case RETURN:
            if (ele){
                this.setHighlightedValue();
                bubble = false;
            }
            break;
        case ESC:
            this.clearSuggestions();
            break;
    }
    return bubble;
}

_bsn.AutoSuggest.prototype.onKeyUp = function(ev)
{
    var key = (window.event) ? window.event.keyCode : ev.keyCode;
    var ARRUP = 38;
    var ARRDN = 40;
    var bubble = true;
    switch(key)
    {
        case ARRUP:
            ev.stopPropagation();
            this.changeHighlight(key);
            bubble = false;
            console.log('key up');
            break;
        case ARRDN:
            ev.stopPropagation();
            this.changeHighlight(key);
            bubble = false;
            console.log('key down');
            break;
        default:
            this.getSuggestions(this.fld.value);
    }
    return bubble;
}

_bsn.AutoSuggest.prototype.getSuggestions = function (val)
{
    
    // if input stays the same, do nothing
    //
    if (val == this.sInput)
        return false;
        
    var pos = val.search('[a-z|A-Z]');
    if (pos>=0)
        val = val.substr(pos,val.length);
    else
        return false;

    // input length is less than the min required to trigger a request
    // reset input string
    // do nothing
    //
    if (val.length < this.oP.minchars)
    {
        this.sInput = "";
        return false;
    }
    
    
    // if caching enabled, and user is typing (ie. length of input is increasing)
    // filter results out of aSuggestions from last request
    //
    if (val.length>this.nInputChars && this.aSuggestions.length && this.oP.cache)
    {
        var arr = [];
        /*
        for (var i=0;i<this.aSuggestions.length;i++)
        {
            if (this.aSuggestions[i].value.substr(0,val.length).toLowerCase() == val.toLowerCase())
                arr.push( this.aSuggestions[i] );
        }
        */
        var pattern = new RegExp('\\b'+val,"i");
        for (var i=0;i<this.aSuggestions.length;i++){
            if (this.aSuggestions[i].value.search(pattern)>=0){
                arr.push( this.aSuggestions[i] );
            }
        }
        
        this.sInput = val;
        this.nInputChars = val.length;
        this.aSuggestions = arr;        
        this.createList(this.aSuggestions);       
        return false;
    }
    else{
        this.sInput = val;
        this.nInputChars = val.length;
        var pointer = this;
        clearTimeout(this.ajID);
        this.ajID = setTimeout( function() { pointer.doAjaxRequest() }, this.oP.delay );
    }
    return false;
}

_bsn.AutoSuggest.prototype.doAjaxRequest = function ()
{
	/*
	var pointer = this;
    chrome.extension.sendRequest({
		action: 'searchcontacts',
		startwithstr: escape(this.sInput) }, 
        function(ajaxreturn) {
            if (ajaxreturn.status==='OK'){
                pointer.idAs = "as_"+String(pointer.fld.seq);
                pointer.aSuggestions = ajaxreturn.data.results;
                pointer.createList(pointer.aSuggestions);
            }
        }
    );
	*/
	var self = this;
	if (typeof(self.oP.searchFunction) == "function"){
		self.oP.searchFunction( 
			escape(this.sInput),
			function(ret){
				if (ret.status==='OK'){
					self.idAs = "as_"+String(self.fld.id);
					self.aSuggestions = ret.data.results;
					self.createList(self.aSuggestions);
				}
			}		
		);	
	}
}

_bsn.AutoSuggest.prototype.createList = function(arr)
{
    var pointer = this;
    // get rid of old list
    // and clear the list removal timeout
    //
    _bsn.DOM.removeElement(this.idAs);
    this.killTimeout();
    // create holding div
    //
    var div = _bsn.DOM.createElement("div", {id:this.idAs, className:this.oP.className} );    
    var hcorner = _bsn.DOM.createElement("div", {className:"as_corner"});
    var hbar = _bsn.DOM.createElement("div", {className:"as_bar"});
    var header = _bsn.DOM.createElement("div", {className:"as_header"});
    header.appendChild(hcorner);
    header.appendChild(hbar);
    div.appendChild(header);   
    // create and populate ul
    //
    var ul = _bsn.DOM.createElement("ul", {id:"as_ul"});    
    // loop throught arr of suggestions
    // creating an LI element for each suggestion
    //
    for (var i=0;i<arr.length;i++){
        // format output with the input enclosed in a EM element
        // (as HTML, not DOM)
        //
        var val = arr[i].value;
        var st = val.toLowerCase().indexOf( this.sInput.toLowerCase() );
        var output = val.substring(0,st) + "<em>" + val.substring(st, st+this.sInput.length) + "</em>" + val.substring(st+this.sInput.length);
        var span         = _bsn.DOM.createElement("span", {}, output, true);
        if (arr[i].info != ""){
            var br            = _bsn.DOM.createElement("br", {});
            span.appendChild(br);
            var small        = _bsn.DOM.createElement("small", {}, arr[i].info);
            span.appendChild(small);
        }        
        var a             = _bsn.DOM.createElement("a", { href:"#" });        
        var tl         = _bsn.DOM.createElement("span", {className:"tl"}, " ");
        var tr         = _bsn.DOM.createElement("span", {className:"tr"}, " ");
        a.appendChild(tl);
        a.appendChild(tr);        
        a.appendChild(span);        
        a.name = i+1;
        
        a.onmousedown = function (ev) { 
            ev.stopPropagation();
            pointer.setHighlightedValue(); 
            return false; 
        }
        a.onclick = function (ev) { 
            ev.stopPropagation();
//            pointer.setHighlightedValue(); 
            return false; 
        }
        a.onmouseover = function (ev) { 
            ev.stopPropagation();
            pointer.setHighlight(this.name); 
            return false;
        }
        
        var li = _bsn.DOM.createElement(  "li", {}, a  );
        ul.appendChild( li );
    }
    // no results
    //
    if (arr.length == 0)
    {
		var li = _bsn.DOM.createElement("li",{});
		if (this.oP.noresultslink===''){
			var li = _bsn.DOM.createElement( "li", {className:"as_warning"}, this.oP.noresults  );
		}
		else{
            var a;
            if (typeof(this.oP.noresultslink) == "function"){
                a = _bsn.DOM.createElement(
                    "a", 
                    { 
                        href: '#',
                        onclick: this.oP.noresultslink,
                        class: 'noresult'
                    },
                    this.oP.noresults 
                );            
            }
            else{
                a = _bsn.DOM.createElement(
                    "a", 
                    { 
                        href:this.oP.noresultslink, 
                        class:'noresult', 
                        target:'_blank'
                    },
                    this.oP.noresults 
                );            
            }
			li.appendChild(a);		
		}		
        ul.appendChild( li );
    }
    div.appendChild( ul );
    var fcorner = _bsn.DOM.createElement("div", {className:"as_corner"});
    var fbar = _bsn.DOM.createElement("div", {className:"as_bar"});
    var footer = _bsn.DOM.createElement("div", {className:"as_footer"});
    footer.appendChild(fcorner);
    footer.appendChild(fbar);
    div.appendChild(footer);
    // get position of target textfield
    // position holding div below it
    // set width of holding div to width of field
    //
    var pos = _bsn.DOM.getPos(this.fld);
    div.style.left         = pos.x + "px";
    div.style.top         = ( pos.y + this.fld.offsetHeight + this.oP.offsety ) + "px";
    div.style.width     = (this.fld.offsetWidth +2) + "px";
    // set mouseover functions for div
    // when mouse pointer leaves div, set a timeout to remove the list after an interval
    // when mouse enters div, kill the timeout so the list won't be removed
    //
    
    div.onmousedown = function (ev) { 
        ev.stopPropagation();
        pointer.setHighlightedValue(); 
        return false; 
    };
    
    div.onmouseover     = function(){ pointer.killTimeout() }
    div.onmouseout         = function(){ pointer.resetTimeout() }
    // add DIV to document
    //
    var parent = this.fld.parentNode;
    parent.appendChild(div);
    //document.getElementsByTagName("body")[0].appendChild(div);
    // currently no item is highlighted
    //
    this.iHighlighted = 0;
    // remove list after an interval
    //
    var pointer = this;
    this.toID = setTimeout(function () { pointer.clearSuggestions() }, this.oP.timeout);
}

_bsn.AutoSuggest.prototype.changeHighlight = function(key)
{    
    var list = _bsn.DOM.getElement("as_ul");
    if (!list)
        return false;
    var n;
    if (key == 40)
        n = this.iHighlighted + 1;
    else if (key == 38)
        n = this.iHighlighted - 1;
    if (n > list.childNodes.length)
        n = list.childNodes.length;
    if (n < 1)
        n = 1;
    this.setHighlight(n);
}

_bsn.AutoSuggest.prototype.setHighlight = function(n)
{
    var list = _bsn.DOM.getElement("as_ul");
    if (!list)
        return false;
    if (this.iHighlighted > 0)
        this.clearHighlight();
    this.iHighlighted = Number(n);
    list.childNodes[this.iHighlighted-1].className = "as_highlight";
    this.killTimeout();
}

_bsn.AutoSuggest.prototype.clearHighlight = function()
{
    var list = _bsn.DOM.getElement("as_ul");
    if (!list)
        return false;
    
    if (this.iHighlighted > 0)
    {
        list.childNodes[this.iHighlighted-1].className = "";
        this.iHighlighted = 0;
    }
}


_bsn.AutoSuggest.prototype.setHighlightedValue = function ()
{
    if (this.iHighlighted)
    {

        var val = this.aSuggestions[ this.iHighlighted-1 ].value;
        var typedstr = this.fld.value;  
        this.sInput = this.fld.value = val;
        
        // move cursor to end of input (safari)
        //
        this.fld.focus();
        if (this.fld.selectionStart)
            this.fld.setSelectionRange(this.sInput.length, this.sInput.length);
        

        this.clearSuggestions();
        
        // pass selected object to callback function, if exists
        //
        if (typeof(this.oP.callback) == "function")
            this.oP.callback( this.aSuggestions[ this.iHighlighted-1 ], typedstr );
    }
}

_bsn.AutoSuggest.prototype.killTimeout = function()
{
    clearTimeout(this.toID);
}

_bsn.AutoSuggest.prototype.resetTimeout = function()
{
    clearTimeout(this.toID);
    var pointer = this;
    this.toID = setTimeout(function () { pointer.clearSuggestions() }, 200);
}

_bsn.AutoSuggest.prototype.clearSuggestions = function ()
{
    this.killTimeout();
    var ele = _bsn.DOM.getElement(this.idAs);
    var pointer = this;
    if (ele)
    {
        var fade = new _bsn.Fader(ele,1,0,250,function () { _bsn.DOM.removeElement(pointer.idAs) });
    }
}

if (typeof(_bsn.DOM) == "undefined")
    _bsn.DOM = {}
    
_bsn.DOM.createElement = function ( type, attr, cont, html )
{
    var ne = document.createElement( type );
    if (!ne)
        return false;        
    for (var a in attr)
        ne[a] = attr[a];        
    if (typeof(cont) == "string" && !html)
        ne.appendChild( document.createTextNode(cont) );
    else if (typeof(cont) == "string" && html)
        ne.innerHTML = cont;
    else if (typeof(cont) == "object")
        ne.appendChild( cont );

    return ne;
}

_bsn.DOM.clearElement = function ( id )
{
    var ele = this.getElement( id );    
    if (!ele)
        return false;
    while (ele.childNodes.length)
        ele.removeChild( ele.childNodes[0] );
    return true;
}

_bsn.DOM.removeElement = function ( ele )
{
    var e = this.getElement(ele);
    if (!e)
        return false;
    else if (e.parentNode.removeChild(e))
        return true;
    else
        return false;
}

_bsn.DOM.replaceContent = function ( id, cont, html )
{
    var ele = this.getElement( id );
    if (!ele)
        return false;    
    this.clearElement( ele );    
    if (typeof(cont) == "string" && !html)
        ele.appendChild( document.createTextNode(cont) );
    else if (typeof(cont) == "string" && html)
        ele.innerHTML = cont;
    else if (typeof(cont) == "object")
        ele.appendChild( cont );
}

_bsn.DOM.getElement = function ( ele )
{
    if (typeof(ele) == "undefined"){
        return false;
    }
    else if (typeof(ele) == "string")
    {
        var re = document.getElementById( ele );
        if (!re)
            return false;
        else if (typeof(re.appendChild) != "undefined" ) {
            return re;
        } else {
            return false;
        }
    }
    else if (typeof(ele.appendChild) != "undefined")
        return ele;
    else
        return false;
}

_bsn.DOM.appendChildren = function ( id, arr )
{
    var ele = this.getElement( id );
    if (!ele)
        return false;        
    if (typeof(arr) != "object")
        return false;        
    for (var i=0;i<arr.length;i++)
    {
        var cont = arr[i];
        if (typeof(cont) == "string")
            ele.appendChild( document.createTextNode(cont) );
        else if (typeof(cont) == "object")
            ele.appendChild( cont );
    }
}

_bsn.DOM.getPos = function ( ele )
{
    var ele = this.getElement(ele);

    var obj = ele;

    var curleft = 0;
    if (obj.offsetParent)
    {
        while (obj.offsetParent)
        {
            curleft += obj.offsetLeft
            obj = obj.offsetParent;
        }
    }
    else if (obj.x)
        curleft += obj.x;


    var obj = ele;
    
    var curtop = 0;
    if (obj.offsetParent)
    {
        while (obj.offsetParent)
        {
            curtop += obj.offsetTop
            obj = obj.offsetParent;
        }
    }
    else if (obj.y)
        curtop += obj.y;

    return {x:curleft, y:curtop}
}

// FADER PROTOTYPE _____________________________________________
if (typeof(_bsn.Fader) == "undefined")
    _bsn.Fader = {}
    
_bsn.Fader = function (ele, from, to, fadetime, callback)
{    
    if (!ele)
        return false;
    this.ele = ele;
    this.from = from;
    this.to = to;
    this.callback = callback;
    this.nDur = fadetime;
    this.nInt = 50;
    this.nTime = 0;
    var p = this;
    this.nID = setInterval(function() { p._fade() }, this.nInt);
}

_bsn.Fader.prototype._fade = function()
{
    this.nTime += this.nInt;
    
    var ieop = Math.round( this._tween(this.nTime, this.from, this.to, this.nDur) * 100 );
    var op = ieop / 100;
    
    if (this.ele.filters) // internet explorer
    {
        try
        {
            this.ele.filters.item("DXImageTransform.Microsoft.Alpha").opacity = ieop;
        } catch (e) { 
            // If it is not set initially, the browser will throw an error.  This will set it if it is not set yet.
            this.ele.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity='+ieop+')';
        }
    }
    else // other browsers
    {
        this.ele.style.opacity = op;
    }
    if (this.nTime == this.nDur)
    {
        clearInterval( this.nID );
        if (this.callback != undefined)
            this.callback();
    }
}



_bsn.Fader.prototype._tween = function(t,b,c,d)
{
    return b + ( (c-b) * (t/d) );
}